package com.weixing.mall.provider.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.weixing.mall.base.exception.BusinessException;
import com.weixing.mall.base.wrapper.Result;
import com.weixing.mall.base.enums.WhetherEnum;
import com.weixing.mall.core.support.auth.AuthUtil;
import com.weixing.mall.core.support.auth.MemAuth;
import com.weixing.mall.core.support.auth.SysAuth;
import com.weixing.mall.provider.api.service.MemberServiceRpc;
import com.weixing.mall.provider.mapper.MediaOrderdRecordMapper;
import com.weixing.mall.provider.mapper.RoleMapper;
import com.weixing.mall.provider.model.domain.*;
import com.weixing.mall.provider.model.dto.ads.MediaOrderdRecordDto;
import com.weixing.mall.provider.model.enums.AppointmentEunm;
import com.weixing.mall.provider.model.enums.IsPayEunm;
import com.weixing.mall.provider.model.enums.ads.AdvertsingEunm;
import com.weixing.mall.provider.model.enums.sys.SysFlag;
import com.weixing.mall.provider.model.query.MediaOrderRecordQuery;
import com.weixing.mall.provider.model.query.MediaOrderRecordsQuery;
import com.weixing.mall.provider.model.vo.FrontMediaRecordVo;
import com.weixing.mall.provider.model.vo.ads.MediaOrderRecordDetilVo;
import com.weixing.mall.provider.model.vo.ads.MediaOrderRecordShowVo;
import com.weixing.mall.provider.model.vo.ads.MediaOrderRecordsVo;
import com.weixing.mall.provider.service.IMediaAdvertsingService;
import com.weixing.mall.provider.service.IMediaLabelService;
import com.weixing.mall.provider.service.IMediaOrderdRecordService;
import com.weixing.mall.provider.service.IMediaPlatformService;
import com.weixing.mall.push.GtComponent;
import com.weixing.mall.redis.RedissonLocker;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 代理商抢购记录 服务实现类
 * </p>
 *
 * @author Small
 * @since 2019-11-18
 */
@Service
public class MediaOrderdRecordServiceImpl extends ServiceImpl<MediaOrderdRecordMapper, MediaOrderdRecord> implements IMediaOrderdRecordService {

    @Autowired
    private RoleMapper sysRoleMapper;
    @Autowired
    private IMediaAdvertsingService mediaAdvertsingService;

    @Autowired
    private IMediaLabelService mediaLabelServiceImpl;

    @Autowired
    private IMediaPlatformService mediaPlatformService;
    @Autowired
    private RedissonLocker redissonLocker;
    @Autowired
    private GtComponent gtComponent;
    @Autowired
    private MemberServiceRpc memberServiceRpc;

    @Override
    public IPage<MediaOrderRecordsVo> selectOfPage(Page initMpPage, MediaOrderRecordsQuery mediaOrderRecordsQuery, Long uid) {

        List<Role> roleList = sysRoleMapper.selectSysRoleByUserId(uid, SysFlag.SYS.getValue());
        int qs = 0;
        ArrayList<Integer> numList = new ArrayList<Integer>();
        for (int i = 0; i < roleList.size(); i++) {
            //角色code是director（广告专员），就只能查自己的
            if ("director".equals(roleList.get(i).getRoleCode())) {
                qs = 1;
            } else if ("customer".equals(roleList.get(i).getRoleCode())) {//角色code是customer（广告客服），查客服所属的
                qs = 2;
            } else {//否则查全部的
                qs = 3;
            }
            numList.add(qs);
        }
        //取出最大值
        int type = Collections.max(numList);
        if (type == 1) {//广告专员
            mediaOrderRecordsQuery.setDirector(uid);
        } else if (type == 2) {//广告客服
            mediaOrderRecordsQuery.setDirector(null);
            mediaOrderRecordsQuery.setCustomerId(uid);
        } else {//全部，自由操控

        }
      List<Integer>  list=mediaOrderRecordsQuery.getRecordStatus();
        if(!ObjectUtil.isEmpty(list)&&list.contains(0)){

            mediaOrderRecordsQuery.setIsAll(1);

        }
        if(ObjectUtil.isNotNull(mediaOrderRecordsQuery.getRecordStatus())){
            if(mediaOrderRecordsQuery.getRecordStatus().size()==0){
                mediaOrderRecordsQuery.setRecordStatus(null);
            }
        }
        IPage<MediaOrderRecordsVo> pageList = baseMapper.selectOfPage(initMpPage, mediaOrderRecordsQuery,mediaOrderRecordsQuery.getRecordStatus());
        if (pageList.getRecords() != null && pageList.getRecords().size() > 0) {
            for (MediaOrderRecordsVo mediaOrderRecordsVo : pageList.getRecords()) {
                int orderNum = baseMapper.selectOrderNum(mediaOrderRecordsVo.getAdId());
                mediaOrderRecordsVo.setOrderNum(orderNum);
            }
        }
        return pageList;
    }

    @Override
    public List<MediaOrderRecordShowVo> getList(Long adId) {
        return baseMapper.getList(adId);
    }

    @Override
    public List<MediaOrderRecordDetilVo> selectListByAgentId(Long agentId) {
        return baseMapper.selectListByAgentId(agentId);
    }

    @Override
    public int selectNum(MediaOrderRecordQuery mediaOrderRecordQuery) {
        return baseMapper.selectNum(mediaOrderRecordQuery);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(MediaOrderdRecordDto mediaOrderdRecordDto, Long uid) {
        MediaOrderdRecord mediaOrderdRecord = new MediaOrderdRecord();
        BeanUtils.copyProperties(mediaOrderdRecordDto, mediaOrderdRecord);
        if (mediaOrderdRecordDto.getStatus() != null) {
            mediaOrderdRecord.setStatus(mediaOrderdRecordDto.getStatus().getValue());
        }
        if (null == mediaOrderdRecordDto.getId()) {
            //TODO 这个方法被app和后台同时公用，确认后台不新增，app访问时现将uid作为代理ID放入新增记录下（如有变动请同步修改）
            mediaOrderdRecord.setAgentId(uid);
            mediaOrderdRecord.setCreatedBy(uid);
            mediaOrderdRecord.setUpdatedBy(uid);
            save(mediaOrderdRecord);
            //更新广告状态
            MediaAdvertsing mediaAdvertsing = new MediaAdvertsing();
            mediaAdvertsing.setId(mediaOrderdRecord.getAdId());
            mediaAdvertsing.setIsFull(WhetherEnum.YES.getValue());
            mediaAdvertsing.setStaus(AdvertsingEunm.STATUS2.getValue());
            mediaAdvertsingService.updateById(mediaAdvertsing);
        } else {
            mediaOrderdRecord.setUpdatedBy(uid);
            if (AppointmentEunm.STATUS6.getValue().equals(mediaOrderdRecord.getStatus()) && mediaOrderdRecord.getAdId() != null) {
                //已收款状态 更新收款时间
                mediaOrderdRecord.setPayTime(new Date());
            }
            //若是主动设置失败则为拒绝  记录拒绝状态
            if (AppointmentEunm.STATUS2.getValue().equals(mediaOrderdRecord.getStatus()) && mediaOrderdRecord.getAdId() != null) {
                mediaOrderdRecord.setRefuseStatus(WhetherEnum.YES.getValue());
            }
            //当代理预约广告绑定时发送推送通知
            if (AppointmentEunm.STATUS3.getValue().equals(mediaOrderdRecord.getStatus()) && mediaOrderdRecord.getAdId() != null) {
                //批量推送消息
                Result<String> result = memberServiceRpc.loadAgentCid(mediaOrderdRecord.getAgentId());
                if (result.isSuccess()){
//                    PushMsg pushMsg = new PushMsg();
//                    pushMsg.setTitle("广告通知");
//                    pushMsg.setBody("您的预约广告已被绑定");
//                    pushMsg.setClick_type("none");
//                    gtComponent.pushToSingleCid(pushMsg,result.getData());
                }
            }
            updateById(mediaOrderdRecord);

            //逻辑变更2020年6月12日
            //当代理预约广告付款时同步广告为付款状态并记录代理预约记录ID，同时同一广告其他未成功付款的代理将被设置为失败状态 ---已废弃
            //当代理预约广告已收款时同步广告为付款状态不在有其他逻辑变更 ---新逻辑
            if (AppointmentEunm.STATUS6.getValue().equals(mediaOrderdRecord.getStatus()) && mediaOrderdRecord.getAdId() != null) {
                //同步广告付款状态
                MediaAdvertsing mediaAdvertsing = new MediaAdvertsing();
                mediaAdvertsing.setId(mediaOrderdRecord.getAdId());
                mediaAdvertsing.setIsAgentPay(IsPayEunm.OPEN.getValue());//1 已付款
                mediaAdvertsing.setBusinessId(mediaOrderdRecord.getId());//预约记录ID值
                //设置下预约成功的代理商ID
                MediaOrderdRecord mor = getById(mediaOrderdRecord.getId());
                if(ObjectUtil.isNotEmpty(mor)){
                    mediaAdvertsing.setOrderdAgent(mor.getAgentId());//代理商ID
                }
                mediaAdvertsingService.updateById(mediaAdvertsing);

            }
            //当代理预约广告绑定时，其他代理商的已预约或复审中预约记录将被设置为失败状态
            if (AppointmentEunm.STATUS3.getValue().equals(mediaOrderdRecord.getStatus()) && mediaOrderdRecord.getAdId() != null) {
                //变更其他代理商预约记录状态
                LambdaUpdateWrapper<MediaOrderdRecord> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
                //二期项目增加付款后的订单进行失败处理，对于不是手工出发的失败，标识为否，及Handling 为0
                lambdaUpdateWrapper.set(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS2.getValue()).set(MediaOrderdRecord::getHandling, WhetherEnum.NO.getValue())
                        .ne(MediaOrderdRecord::getId, mediaOrderdRecord.getId())
                        .eq(MediaOrderdRecord::getAdId, mediaOrderdRecord.getAdId())
                        .and(i -> i.eq(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS1.getValue())
                                .or().eq(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS4.getValue()));
                update(lambdaUpdateWrapper);
            }
            //点击解绑时这是传值状态为复审中 将除了已取消的和已拒绝的都设置为复审中状态
            if (AppointmentEunm.STATUS4.getValue().equals(mediaOrderdRecord.getStatus()) && mediaOrderdRecord.getAdId() != null) {
                //变更其他代理商预约记录状态
                LambdaUpdateWrapper<MediaOrderdRecord> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
                lambdaUpdateWrapper.set(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS4.getValue())
                        .ne(MediaOrderdRecord::getId, mediaOrderdRecord.getId())
                        .ne(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS5.getValue())
                        .ne(MediaOrderdRecord::getRefuseStatus, WhetherEnum.YES)
                        .eq(MediaOrderdRecord::getAdId, mediaOrderdRecord.getAdId());
                update(lambdaUpdateWrapper);
            }
            //已排版状态时需要同步广告主状态为已排版状态
            if (AppointmentEunm.STATUS8.getValue().equals(mediaOrderdRecord.getStatus()) && mediaOrderdRecord.getAdId() != null) {
                MediaAdvertsing mediaAdvertsing = new MediaAdvertsing();
                mediaAdvertsing.setId(mediaOrderdRecord.getAdId());
                mediaAdvertsing.setIsCompanyPay(IsPayEunm.OPEN.getValue());
                mediaAdvertsing.setStaus(AdvertsingEunm.STATUS3.getValue());
                mediaAdvertsingService.updateById(mediaAdvertsing);
            }
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveMediaOrderdRecord(MediaOrderdRecordDto mediaOrderdRecordDto) throws Exception {
        MemAuth memAuth = AuthUtil.getMemberExt();
        if(ObjectUtil.isEmpty(memAuth) || memAuth.getAbid() == null||memAuth.getAbads() == null|| memAuth.getAbads() != 1){//只有1代表拥有预约广告的权限
            throw new BusinessException("您没有预约广告权限！");
        }
        Long agentId = memAuth.getAbid();
        boolean lockFlag = redissonLocker.tryLock("mediaOrder:" + mediaOrderdRecordDto.getAdId(), TimeUnit.SECONDS, 10, 10);
        if (lockFlag) {
            try {
                if(!adsLimit(agentId,memAuth.getAdsnum())){
                    throw new BusinessException("您的预约广告数量已达到上限！");
                }
                MediaOrderdRecord mediaOrderdRecord = new MediaOrderdRecord();
                BeanUtils.copyProperties(mediaOrderdRecordDto, mediaOrderdRecord);
                if (ObjectUtil.isNotEmpty(mediaOrderdRecord) && IsCan(mediaOrderdRecord.getAdId(), agentId)) {
                    throw new BusinessException("不可重复预约！");
                }
                MediaAdvertsing mediaAdvertsing = mediaAdvertsingService.getById(mediaOrderdRecord.getAdId());
                if(AdvertsingEunm.STATUS4.getValue().equals(mediaAdvertsing.getStaus())){
                    throw new BusinessException("广告已撤回不可预约！");
                }
                if (ObjectUtil.isNotEmpty(mediaAdvertsing) && mediaAdvertsing.getBusinessId() != null) {
                    throw new BusinessException("广告已被预订！");
                }
                int num = checkNum(mediaOrderdRecord.getAdId());
                MediaPlatform platform = mediaPlatformService.getById(mediaAdvertsing.getPlatform());
                MediaLabel label = mediaLabelServiceImpl.getById(platform.getMediaLabel());
                if(ObjectUtil.isEmpty(label)){
                    throw new BusinessException("广告标签信息有误，暂无法预约");
                }
                if (num >= label.getOrderLimit()) {
                    throw new BusinessException("预约人数已达目标完成，不可在预约");
                }
                //删除当前代理商此广告被取消的记录
                delCancalMediaOrderdRecord(mediaOrderdRecord.getAdId(), agentId);
                //判断广告是否已被绑定  被绑定的话则预约人状态为复审中 没有被绑定就是预约中-----需求废弃
                /*if (IsBindAgent(mediaAdvertsing.getId())) {
                    mediaOrderdRecord.setStatus(AppointmentEunm.STATUS4.getValue());
                } else {
                    mediaOrderdRecord.setStatus(AppointmentEunm.STATUS1.getValue());
                }*/
                //新判断逻辑 广告若是进入绑定或者之后的状态 直接将预约状态置位失败
                if (IsAppointmentSuccess(mediaAdvertsing.getId())) {
                    mediaOrderdRecord.setStatus(AppointmentEunm.STATUS1.getValue());
                } else {
                    mediaOrderdRecord.setStatus(AppointmentEunm.STATUS2.getValue());
                }
                mediaOrderdRecord.setAgentId(agentId);
                mediaOrderdRecord.setCreatedBy(agentId);
                mediaOrderdRecord.setUpdatedBy(agentId);
                save(mediaOrderdRecord);
                //更新广告状态
                if (label.getOrderLimit() - num <= 1) {
                    mediaAdvertsing.setIsFull(WhetherEnum.YES.getValue());
                    mediaAdvertsing.setStaus(AdvertsingEunm.STATUS2.getValue());
                } else {
                    mediaAdvertsing.setStaus(AdvertsingEunm.STATUS2.getValue());
                }
                mediaAdvertsingService.updateById(mediaAdvertsing);
            } catch (Exception e) {
                e.printStackTrace();
                throw new BusinessException(e.getMessage());
            } finally {
                redissonLocker.unlock("mediaOrder:" + mediaOrderdRecordDto.getAdId());
            }
        } else {
            throw new BusinessException("预约失败，请重试！");
        }
    }

    private boolean IsAppointmentSuccess(Long adid){//是否可以预约成功
        return count(new LambdaQueryWrapper<MediaOrderdRecord>()
                .eq(MediaOrderdRecord::getAdId, adid).and(i->
                i.eq(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS3.getValue())
                .or().eq(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS6.getValue())
                .or().eq(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS7.getValue())
                .or().eq(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS8.getValue())
                .or().eq(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS9.getValue()))) < 1 ? true : false;
}

    private boolean IsBindAgent(Long adid) {
        return count(new LambdaQueryWrapper<MediaOrderdRecord>()
                .eq(MediaOrderdRecord::getAdId, adid)
                .eq(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS3.getValue())) > 0 ? true : false;
    }

    private boolean IsCan(Long adid, Long agentId) {
        return count(new LambdaQueryWrapper<MediaOrderdRecord>()
                .eq(MediaOrderdRecord::getAdId, adid)
                .ne(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS5.getValue())
                .eq(MediaOrderdRecord::getAgentId, agentId)) > 0 ? true : false;
    }

    private boolean adsLimit( Long agentId,Integer adsLimit){//已失败、已取消、已投放的排除
        return count(new LambdaQueryWrapper<MediaOrderdRecord>()
                .eq(MediaOrderdRecord::getAgentId, agentId)
                .ne(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS2.getValue())
                .ne(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS5.getValue())
                .ne(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS9.getValue())) < adsLimit ? true : false;
    }

    /**
     * 删除指定的已取消的广告记录
     *
     * @param adid
     * @param agentId
     */
    private void delCancalMediaOrderdRecord(Long adid, Long agentId) {
        if (adid != null && agentId != null) {
            remove(new LambdaQueryWrapper<MediaOrderdRecord>()
                    .eq(MediaOrderdRecord::getAdId, adid)
                    .eq(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS5.getValue())
                    .eq(MediaOrderdRecord::getAgentId, agentId));
        }
    }

    private int checkNum(Long adid) {
        return count(new LambdaQueryWrapper<MediaOrderdRecord>()
                .eq(MediaOrderdRecord::getAdId, adid)
                .ne(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS5.getValue()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeRecordStatus(long id, int value) {
        MediaOrderdRecord mor = getById(id);
        if (ObjectUtil.isEmpty(mor)) {
            throw new BusinessException("预约信息不存在！");
        }
        //若是已绑定状态值流程以上则不可以取消
        List<Integer> statusList = Lists.newArrayList();
        statusList.add(AppointmentEunm.STATUS3.getValue());
        statusList.add(AppointmentEunm.STATUS6.getValue());
        statusList.add(AppointmentEunm.STATUS7.getValue());
        statusList.add(AppointmentEunm.STATUS8.getValue());
        statusList.add(AppointmentEunm.STATUS9.getValue());
        if (statusList.contains(mor.getStatus())) {
            throw new BusinessException("当前预约状态不可取消！");
        }
        mor.setStatus(value);
        updateById(mor);
        //取消预约记录后需要将广告预约人数已满状态恢复成未满员
        if(mor.getAdId() != null){
            MediaAdvertsing ma = new MediaAdvertsing();
            ma.setId(mor.getAdId());
            ma.setIsFull(WhetherEnum.NO.getValue());
            mediaAdvertsingService.updateById(ma);
        }
        //baseMapper.changeRecordStatus(id, value);
    }

    @Override
    public IPage<FrontMediaRecordVo> selectMyMediaOrder(Page page, MediaOrderRecordQuery mediaOrderRecordQuery, Long agentId) {
        mediaOrderRecordQuery.setAgentId(agentId);
        return baseMapper.selectMyMediaOrder(page, mediaOrderRecordQuery);
    }

    /**
     * （广告撤销）批量将预约记录状态变更为失败根据广告ID，取消的记录不在变更
     *
     * @param adId
     * @param operatorId
     */
    @Override
    public void updateFaildStatusBatch(Long adId, Long operatorId) {
        if (adId != null) {
            update(new LambdaUpdateWrapper<MediaOrderdRecord>()
                    .eq(MediaOrderdRecord::getAdId, adId)
                    .ne(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS5.getValue())
                    .set(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS2.getValue())
                    .set(MediaOrderdRecord::getUpdatedBy, operatorId));
        }
    }

    /**
     * 投放广告
     *
     * @param adId
     */
    @Override
    public void advertising(Long adId) {
        MediaAdvertsing ma = mediaAdvertsingService.getById(adId);
        if (ObjectUtil.isEmpty(ma)) {
            throw new BusinessException("广告信息不存在，投放失败");
        }
        if (ma.getBusinessId() == null) {
            throw new BusinessException("预约信息不存在，投放失败");
        }
        update(new LambdaUpdateWrapper<MediaOrderdRecord>()
                .set(MediaOrderdRecord::getStatus, AppointmentEunm.STATUS9.getValue())
                .eq(MediaOrderdRecord::getId, ma.getBusinessId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchUpdateStatus(MediaOrderdRecordDto mord) {
        SysAuth auth = AuthUtil.getSysExt();
        if(auth == null){
            throw new BusinessException("认证失败，请刷新后重试！");
        }
        if(mord == null || mord.getList() == null || mord.getList().size() == 0){
            throw new BusinessException("操作失败，无可操作数据！");
        }
        List<MediaOrderdRecordDto> list = mord.getList();
        List<Long> adIds = Lists.newArrayList();
        for (MediaOrderdRecordDto m:list){
            if(ObjectUtil.isEmpty(m.getStatus())){
                throw new BusinessException("状态不匹配不可操作！");
            }
            //第一个条件 判断变更状态跟当前状态是否匹配  目前是三种状态  批量收款、付款、排版
            Boolean isMatch = true;
            switch (m.getStatus()){
                case STATUS6:
                    if(!AppointmentEunm.STATUS3.getValue().equals(m.getCurrentStatus().getValue())){
                        isMatch = false;
                    }
                    break;
                case STATUS7:
                    if(!AppointmentEunm.STATUS6.getValue().equals(m.getCurrentStatus().getValue())){
                        isMatch = false;
                    }
                    break;
                case STATUS8:
                    if(!AppointmentEunm.STATUS7.getValue().equals(m.getCurrentStatus().getValue())){
                        isMatch = false;
                    }
                    break;
                default:
                        isMatch = false;
                        break;
            }
            if(!isMatch){
                throw new BusinessException("状态不匹配不可操作！");
            }
            //第二个条件 判断批量操作广告ID是否重复
            for (Long adid:adIds){
                if(adid.equals(m.getAdId())){
                    throw new BusinessException("广告ID重复不可操作！");
                }
            }
            adIds.add(m.getAdId());
        }
        //条件校验完毕 更新操作
        for(MediaOrderdRecordDto m:list){
            save(m, auth.getUid());
        }
    }

    @Override
    public MediaOrderRecordDetilVo lastApplyTime(Long id, Long agentId) {
        return baseMapper.lastApplyTime(id, agentId);
    }
}
